package main.java.creatures;

import static java.lang.Math.abs;
import static main.java.commons.Utils.filter;

import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

import main.java.commons.Utils.Predicate;
import main.java.creatures.seed.ISeed;
import creatures.AbstractCreature;
import creatures.ICreature;
import creatures.IEnvironment;
/**
 * 
 * Class AbstractCreatureEcoSystem , represent a creature in the plugin ecosystem
 * {@link AbstractCreature}
 *
 */
public abstract class AbstractCreatureEcoSystem extends AbstractCreature
		implements IAround {

	/**
	 * Define the class which determine the tree of class acceptable
	 * {@link List}	 
	 * {@link Predicate}
	 * 
	 */
	
	protected final List<Class<? extends ICreature>> listClassSystem;

	
	/**
	 * Number max seed per creatures
	 */
	protected int NUMBER_MAX_SEED = 3;
	
	protected BlockingQueue<ISeed> listSeed  = new ArrayBlockingQueue<ISeed>(NUMBER_MAX_SEED);
	
	public final ISeed getSeed() {
		return listSeed.poll();
	}

	
	static class CreaturesAroundCreature implements Predicate<ICreature> {
		private final AbstractCreatureEcoSystem observer;
		
		
		public CreaturesAroundCreature(
				AbstractCreatureEcoSystem abstractCreatureEcoSystem) {
			this.observer = abstractCreatureEcoSystem;
		}
		

		public boolean apply(ICreature input) {
			if (input == observer) {
				return false;
			}
			double dirAngle = input.directionFormAPoint(observer.getPosition(),
					observer.getDirection());
			
			return abs(dirAngle) < (observer.getFieldOfView() / 2)
					&& observer.distanceFromAPoint(input.getPosition()) <= observer.getLengthOfView();

		}
	}

	/**
	 * Verify life If, it's less than 0, we kill the creature.
	 */
	protected void verifyLife() {
		if (life <= 0) {
			this.environment.removeCreature(this);
		}
	}

	public AbstractCreatureEcoSystem(IEnvironment environment, Point2D position) {
		super(environment, position);
		this.listClassSystem = new ArrayList<Class<? extends ICreature>>();
		this.listClassSystem.add(AbstractCreatureEcoSystem.class);
	}

	/**
	 * Get creatures around current object according to type restriction
	 * @param abstractCreatureEcoSystem
	 * @return Iterable of creature
	 */
	public Iterable<ICreature> creaturesAround(
			AbstractCreatureEcoSystem abstractCreatureEcoSystem) {
		return filter(
				filter(environment.getCreatures(), new CreaturesAroundCreature(
						this)), this.getPredicate());
	}

	/**
	 * Define the element of environnment
	 * 
	 * @return Predicate<ICreature>
	 * {@link Predicate}
	 */
	protected Predicate<ICreature> getPredicate() {
		return new Predicate<ICreature>() {

			@Override
			public boolean apply(ICreature input) {
				for (Class<? extends ICreature> p : listClassSystem) {
					if (!p.isAssignableFrom(input.getClass())) {
						return false;
					}
				}
				return true;
			}
		};
	}

}
